<template>
  <div ref="view" class="pull-down">
    <div
      ref="container"
      class="pull-down-container"
      :style="containerStyle"
      @touchstart="onTouchStart"
      @touchmove="onTouchMove"
      @touchend="onTouchEnd"
      @touchcancel="onTouchEnd"
    >
      <div class="pull-down-header" :style="headerStyle">
        <slot name="header">
          <div class="pull-down-header-default">
            <pull-box-load-circle
              class="pull-down-header-default-progress"
              :radius="15"
              :progress="isBacking ? lastProcess : (positionY / height)"
            />
            <p class="pull-down-text mu-text-primary-color">{{ pullDownText }}</p>
          </div>
        </slot>
      </div>
      <div ref="content" class="pull-down-content">
        <p v-if="!loading && hasMore && showLoadLink" class="load-link">
          <a href="javascript:;" @click="$emit('load')">点击加载更多</a>
        </p>
        <slot name="default">
          <p class="pull-down-text">没有数据</p>
        </slot>
      </div>
    </div>
  </div>
</template>

<script>
import PullBoxLoadCircle from './PullBoxLoadCircle'

// https://www.cnblogs.com/aser1989/p/4560749.html
const isTouchPad = (/hp-tablet/gi).test(navigator.appVersion)
const hasTouch = 'ontouchstart' in window && !isTouchPad

function getPageY(event) {
  return (hasTouch && event instanceof TouchEvent) ? event.touches[0].pageY : event.pageY
}

export default {
  name: 'PullDown',
  components: {
    PullBoxLoadCircle
  },
  props: {
    loading: {
      type: Boolean,
      required: true
    },
    hasMore: {
      type: Boolean,
      default: true
    },
    showLoadLink: {
      type: Boolean,
      default: false
    },
    height: {
      type: Number,
      default: 50
    },
    triggerOffset: {
      type: Number,
      default: 0
    },
    dampOffset: {
      type: Number,
      default: 0
    },
    backTime: {
      type: Number,
      default: 0.6
    },
    delayTime: {
      type: Number,
      default: 0.4
    },
    loadText: {
      type: String,
      default: '下拉刷新'
    },
    loadingText: {
      type: String,
      default: '加载中'
    },
    noMoreText: {
      type: String,
      default: '没有啦'
    }
  },
  data() {
    return {
      positionY: 0,
      transitionTime: 0,
      start: 0,
      end: 0,
      isPulling: false,
      isBacking: false,
      lastProcess: 0
    }
  },
  computed: {
    containerStyle() {
      return {
        'transform': `translateY(${this.positionY - this.height}px)`,
        'transition': `transform ${this.transitionTime}s`,
        'height': `calc(100% + ${this.height}px)`
      }
    },
    headerStyle() {
      return {
        'height': `${this.height}px`
      }
    },
    logRate() {
      const realMax = 640 // 常量，大约为人手最多能滑动的距离
      const reflectMax = this.height - this.dampOffset // 允许滑动的最大距离
      return Math.log(realMax) / reflectMax // log(a)
    },
    raduis() {
      return this.height * 0.4
    },
    pullDownText() {
      if (this.hasMore) {
        return (this.loading || this.isBacking) ? this.loadingText : this.loadText
      } else {
        return this.noMoreText
      }
    }
  },
  created() {
    if (this.triggerOffset === 0) {
      this.triggerOffset = this.height * 0.9 // 默认滑动超90%即触发
    }
    if (this.dampOffset === 0) {
      this.dampOffset = this.height * 0.5 // 默认从一半的位置开始难以拖动
    }
  },
  methods: {
    onTouchStart(event) {
      if (this.$refs.container.scrollTop <= 0) {
        this.start = getPageY(event)
        this.transitionTime = 0
        this.isPulling = true
      }
    },
    onTouchMove(event) {
      if (this.$refs.container.scrollTop <= 0 && this.isPulling === true) {
        this.end = getPageY(event)
        if (this.start < this.end) {
          event.preventDefault()
          const move = this.end - this.start
          // 根据移动距离，给出难以拖动的效果
          if (move >= this.dampOffset) {
            const yReal = move - this.dampOffset
            const yReflect = Math.log(yReal) / this.logRate
            this.positionY = this.dampOffset + yReflect
          } else {
            this.positionY = move
          }
        }
      }
    },
    onTouchEnd(event) {
      if (this.isBacking || this.loading) {
        if (this.positionY > 0) {
          this.isBacking = false
          this.rollback(0, this.backTime)
        }
        return
      }
      if (this.end - this.start >= this.triggerOffset) {
        this.lastProcess = 1
        this.start = 0
        this.end = 0
        this.$emit('load')
        this.rollback(this.delayTime, this.backTime)
      } else {
        this.rollback(0, this.backTime)
      }
    },
    rollback(delayTime, backTime) {
      if (this.isBacking) {
        return
      }
      this.isBacking = true
      setTimeout(() => {
        this.transitionTime = backTime
        this.$nextTick(() => {
          setTimeout(() => {
            this.isBacking = false
          }, backTime * 1000)
          this.positionY = 0
        })
        this.isPulling = false
      }, delayTime * 1000)
    },
    // 获取剩余高度
    getRemainHeight() {
      const { scrollTop } = this.$refs.container
      const { offsetHeight: contentHeight } = this.$refs.content
      const { offsetHeight: viewHeight } = this.$refs.view
      const remain = contentHeight - scrollTop - viewHeight
      return remain
    },
    // 设置剩余高度
    setRemainHeight(remain) {
      if (remain < 0) {
        remain = 0
      }
      const { offsetHeight: contentHeight } = this.$refs.content
      const { offsetHeight: viewHeight } = this.$refs.view
      this.$refs.container.scrollTop = contentHeight - remain - viewHeight
    }
  }
}
</script>

<style scoped>
.pull-down {
  overflow: hidden;
  width: 100%;
  height: 100%;
}
.pull-down-container {
  overflow-y: scroll;
  width: 100%;
}
.pull-down-text {
  padding: 0 12px;
  text-align: center;
}
.pull-down-header,
.pull-down-content {
  width: 100%;
}

.pull-down-header-default {
  height: 100%;
  display: flex;
  flex-flow: row nowrap;
  justify-content: center;
  align-items: center;
  background: linear-gradient(to bottom, #e0e0e0 0%, #eaedf4 100%);
}
.pull-down-header-default-progress {
  transform: rotate(-90deg);
}
.load-link {
  text-align: center;
  font-size: 12px;
  padding: 4px 0;
}
</style>
